//------------------------------------------------------------------------------
//
// ...
// classname: BitmapUtil
// author: 小兵( blog.csdn.net/aosnowasp )
// created: 2014-11-18
// copyright (c) 2013 小兵( aosnow@yeah.net )
//
//------------------------------------------------------------------------------

package starfire.utils.bitmaps
{
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.geom.Point;
	import flash.geom.Rectangle;

	import starfire.utils.maths.min;

	public class BitmapUtil
	{
		//--------------------------------------------------------------------------
		//
		//  Class constants
		//
		//--------------------------------------------------------------------------

		// 辅助对象
		protected static const SHAPE_H1:Shape = new Shape();
		protected static const SHAPE_H2:Shape = new Shape();

		protected static const POT_BASE:Point = new Point();
		protected static const POT:Point = new Point();

		//--------------------------------------------------------------------------
		//
		//	Class methods
		//
		//--------------------------------------------------------------------------

		/**
		 * 根据两个点之间的连线来切割指定的位图
		 * <p><b>注意：</b>两个点必须与源始位图区域矩形不同的两条边相交，否则将返回空。</p>
		 * @param source 源始位图数据
		 * @param x1 点1的X位置
		 * @param y1 点1的Y位置
		 * @param x2 点2的X位置
		 * @param y2 点2的Y位置
		 * @return 切割后的两个新位图，返回结果将遵循从左至右，从上到下的原则进行排序
		 */
		public static function cutBitmapData( source:BitmapData, x1:Number, y1:Number, x2:Number, y2:Number, actualSize:Boolean = true ):Vector.<BitmapData>
		{
			var r:Vector.<BitmapData> = new Vector.<BitmapData>();

			if( source )
			{
				// 保存属性值，避免频繁引用，可提高效率
				var w:Number = source.width;
				var h:Number = source.height;

				// 确定两个点是否与矩形边相交
				if(( x1 == 0 || x1 == w || y1 == 0 || y1 == h ) && //
				( x2 == 0 || x2 == w || y2 == 0 || y2 == h ) && //
				( x1 != x2 && ( y1 != y2 || y1 != 0 && y1 != h )) && //
				( y1 != y2 && ( x1 != x2 || x1 != 0 && x1 != w )))
				{
					// 分析矩形四个角，以及指定用于连线的两个点的关系和顺序
					var ps:Array = [];
					ps.push( 0, 0 );

					if( x1 > 0 && x1 < w && y1 == 0 )
					{
						ps.push( x1, y1 );
					}
					else if( x2 > 0 && x2 < w && y2 == 0 )
					{
						ps.push( x2, y2 );
					}

					ps.push( w, 0 );

					if( y1 > 0 && y1 < h && x1 == w )
					{
						ps.push( x1, y1 );
					}
					else if( y2 > 0 && y2 < h && x2 == w )
					{
						ps.push( x2, y2 );
					}

					ps.push( w, h );

					if( x1 > 0 && x1 < w && y1 == h )
					{
						ps.push( x1, y1 );
					}
					else if( x2 > 0 && x2 < w && y2 == h )
					{
						ps.push( x2, y2 );
					}

					ps.push( 0, h );

					if( y1 > 0 && y1 < h && x1 == 0 )
					{
						ps.push( x1, y1 );
					}
					else if( y2 > 0 && y2 < h && x2 == 0 )
					{
						ps.push( x2, y2 );
					}

					// 拆分把得到的点分配给两个不同的数组，这样就正好是两个Shape的各个点
					var ps2:Array = [];
					var index:Array = [];
					var i:int = 0;

					// 找出两个指定点在参照点集合中的位置
					for( i = 0; i < ps.length; i += 2 )
					{
						if( ps[ i ] == x1 && ps[ i + 1 ] == y1 || ps[ i ] == x2 && ps[ i + 1 ] == y2 )
						{
							index.push( i );

							if( index.length == 2 )
								break;
						}
					}

					// 排序按从小到大
					index.sort( Array.NUMERIC );

					// slice 第二个参数所指定位置数据不包含，所以要多加1
					ps2 = ps.slice( index[ 0 ], index[ 1 ] + 2 );

					// index[ 1 ] - index[ 0 ] - 2 由 index[ 1 ] + 1 - index[ 0 ] + 1 - 4 优化所得
					ps.splice( index[ 0 ] + 2, index[ 1 ] - index[ 0 ] - 2 );

					// 绘制图形一
					SHAPE_H1.graphics.clear();
					SHAPE_H1.graphics.beginFill( 0x0 );
					SHAPE_H1.graphics.moveTo( ps[ 0 ], ps[ 1 ]);

					var lng:int = ps.length;

					for( i = 2; i < lng; i += 2 )
					{
						SHAPE_H1.graphics.lineTo( ps[ i ], ps[ i + 1 ]);
					}
					SHAPE_H1.graphics.endFill();

					// 绘制图形二（x1,y1到x2,y2结束）
					SHAPE_H2.graphics.clear();
					SHAPE_H2.graphics.beginFill( 0xffcc00 );
					SHAPE_H2.graphics.moveTo( ps2[ 0 ], ps2[ 1 ]);

					lng = ps2.length;

					for( i = 2; i < lng; i += 2 )
					{
						SHAPE_H2.graphics.lineTo( ps2[ i ], ps2[ i + 1 ]);
					}
					SHAPE_H2.graphics.endFill();

					// 复制位图数据，并结合上面的图形

					if( x1 == 0 && y1 == 0 || x2 == 0 && y2 == 0 )
					{
						// 若指定两个点中有一个点从 0,0 点开始，则把这块切图排列在前
						r.push( copyPixelsWithShape( source, SHAPE_H2, 0, 0, actualSize ));
						r.push( copyPixelsWithShape( source, SHAPE_H1, 0, 0, actualSize ));
					}
					else
					{
						// 默认都把切图一排列在前
						// 计算第二个切图的偏移
						var offsetX:Number = SHAPE_H2.width != w ? min( x1, x2 ) : 0;
						var offsetY:Number = SHAPE_H2.height != h ? min( y1, y2 ) : 0;

						r.push( copyPixelsWithShape( source, SHAPE_H1, 0, 0, actualSize )); // 必定包含 0,0 点，不必计算偏移
						r.push( copyPixelsWithShape( source, SHAPE_H2, offsetX, offsetY, actualSize ));
					}

					// 清除临时数据
					ps.length = 0;
					ps2.length = 0;
					index.length = 0;
					ps = null;
					ps2 = null;
					index = null;
				}
			}

			return r;
		}

		/**
		 * 根据指定的图形为基础，裁剪指定的源位图数据并返回
		 * <p>在绘制 Shape 图时，你可以在任意位置绘制，但一般需要保证绘制内容限制在与位图范围相同的矩形区域内。</p>
		 * @param source 源位图数据
		 * @param shape 用于遮挡的基础图形（Shape或者BitmapData）
		 * @param offsetX X偏移值，即遮挡图形位于source源图上的X位置
		 * @param offsetY Y偏移值，即遮挡图形位于source源图上的Y位置
		 * @param actualSize 是否生成与参考 shape 图大小相同的位图结果（若为 false，则返回结果与源位图大小一致）
		 * @return 返回裁剪后的新位图数据对象，若 shape 参数不是有效的 Shape或者BitmapData，则返回 null
		 */
		public static function copyPixelsWithShape( source:BitmapData, shape:*, offsetX:Number = 0, offsetY:Number = 0, actualSize:Boolean = true ):BitmapData
		{
			var bit:BitmapData;

			if( shape is Shape )
			{
				// 图形可以位于源位图中的任意位置，所以此处定义绘制的大小与源位图相同
				bit = new BitmapData( source.width, source.height, true, 0x00ffffff );
				bit.draw( shape );
			}
			else if( shape is BitmapData )
			{
				bit = shape;
			}
			else
			{
				return null;
			}

			// 按指定的图形裁剪原始位图得到结果
			var r:BitmapData = new BitmapData( source.width, source.height, true, 0x00ffffff );
			r.copyPixels( source, source.rect, POT_BASE, bit, null, true );

			// 去除透明区域，保留实际大小的位图内容
			if( actualSize )
			{
				var rt:BitmapData = new BitmapData( shape.width, shape.height, true, 0x00ffffff );
				var rect:Rectangle = r.rect;

				rect.setTo( offsetX, offsetY, shape.width, shape.height );
				rt.copyPixels( r, rect, POT_BASE );

				r.dispose();
				r = rt;
			}

			// 若是在该方法内临时建立的位图数据则清除掉
			if( shape is Shape )
			{
				bit.dispose();
				bit = null;
			}

			return r;
		}

		/**
		 * 取得指定矩形区域内，任意两边上随机点所形成连线数据
		 * @param rect 指定矩形区域
		 * @return 返回 starfire.utils.bitmaps.SimpleLine 数据对象
		 */
		public static function getRandLineInRect( rect:Rectangle ):SimpleLine
		{
			var ps:Array = [ rect.x, rect.y, rect.right, rect.y, rect.right, rect.bottom, rect.x, rect.bottom ];
			var ia:int = Math.random() * 4 >> 0; // 随机取一点的索引
			var indexs:Array = [ 0, 1, 2, 3 ]; // 四个顶点的索引

			// 为了不随机到同一条边上，去除已取到点的索引
			indexs.splice( indexs.indexOf( ia ), 1 );

			// 取第二点索引
			var ib:int = indexs[ Math.random() * 3 >> 0 ];

			// 确定随机范围（自动向后延伸到第二个顶点）
			var line1:SimpleLine = new SimpleLine();
			var id:int = ia * 2;
			line1.xa = ps[ id ];
			line1.ya = ps[ id + 1 ];

			if( ia == 3 )
			{
				line1.xb = ps[ 0 ];
				line1.yb = ps[ 1 ];
			}
			else
			{
				line1.xb = ps[ id + 2 ];
				line1.yb = ps[ id + 3 ];
			}

			var line2:SimpleLine = new SimpleLine();
			id = ib * 2;
			line2.xa = ps[ id ];
			line2.ya = ps[ id + 1 ];

			if( ib == 3 )
			{
				line2.xb = ps[ 0 ];
				line2.yb = ps[ 1 ];
			}
			else
			{
				line2.xb = ps[ id + 2 ];
				line2.yb = ps[ id + 3 ];
			}

			// 生成连线
			var line:SimpleLine;

			line = new SimpleLine();
			line.xa = line1.xa == line1.xb ? line1.xa : Math.random() * ( line1.xa - line1.xb ) + line1.xb >> 0;
			line.ya = line1.ya == line1.yb ? line1.ya : Math.random() * ( line1.ya - line1.yb ) + line1.yb >> 0;

			line.xb = line2.xa == line2.xb ? line2.xa : Math.random() * ( line2.xa - line2.xb ) + line2.xb >> 0;
			line.yb = line2.ya == line2.yb ? line2.ya : Math.random() * ( line2.ya - line2.yb ) + line2.yb >> 0;

			return line;
		}
	}
}
